#include <iostream>
#include <mutex>

#include <include/core/SkCanvas.h>
#include <include/core/SkBitmap.h>
#include <include/core/SkImage.h>
#include <include/core/SkPicture.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <include/gpu/gl/GrGLInterface.h>
#include <include/gpu/GrDirectContext.h>
#include <include/core/SkSurface.h>

#include <skia_framework.h>

// #define SHOW_GLFW_SHADOW_WINDOW

int main()
{
    std::mutex winPropsMutex;
    int winWidth = 640;
    int winHeight = 480;

    if (!glfwInit()) {
        std::cerr << "glfwInit failed\n";
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#if !defined(SHOW_GLFW_SHADOW_WINDOW)
    // invisible
    glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
#endif

    GLFWwindow* window = glfwCreateWindow(winWidth, winHeight, "glfw shadow window", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "glfwCreateWindow failed" << std::endl;
        std::cerr << "errno: " << errno << ", means " << strerror(errno) << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    sk_sp<const GrGLInterface> glinterface{GrGLCreateNativeInterface()};
    if (glinterface == nullptr) {
        std::cerr << "glinterface == nullptr\n";
        return -1;
    }

    const auto &grContext = GrDirectContext::MakeGL(glinterface);
    if (grContext == nullptr) {
        std::cerr << "grContext == nullptr\n";
        return -1;
    }

    sk_sp<SkSurface> skSurface = nullptr;
    SkBitmap bitmap;

    auto onResize = [&](int width, int height) {
        std::lock_guard lock(winPropsMutex);
        winWidth = width;
        winHeight = height;

        // surface
        SkColorType colorType = kRGBA_8888_SkColorType;
        sk_sp<SkColorSpace> skColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
        SkSurfaceProps surfaceProps;
        GrBackendRenderTarget backendRenderTarget(winWidth, winHeight, 0, 8, {.fFBOID = 0, .fFormat = GL_RGBA8});
        skSurface = SkSurface::MakeFromBackendRenderTarget(grContext.get(),
                                                           backendRenderTarget,
                                                           kTopLeft_GrSurfaceOrigin,
                                                           colorType, skColorSpace, &surfaceProps);

        // bitmap
        bitmap.allocPixels(SkImageInfo::Make(winWidth, winHeight, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType));

        // clear canvas
        const auto &surfaceCanvas = skSurface->getCanvas();
        if (surfaceCanvas == nullptr) {
            std::cerr << "canvas == nullptr\n";
            return;
        }
        surfaceCanvas->clear(SK_ColorTRANSPARENT);

        // draw call
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(paint.kStroke_Style);
        paint.setARGB(0xff, 0xff, 0, 0);
        surfaceCanvas->drawLine({0, 0}, {1000, 1000}, paint);
    };

    auto onDraw = [&](SkCanvas &canvas) {
        std::lock_guard lock(winPropsMutex);
        glfwMakeContextCurrent(window);

        const auto &surfaceCanvas = skSurface->getCanvas();
        if (surfaceCanvas == nullptr) {
            std::cerr << "canvas == nullptr\n";
            return;
        }
        surfaceCanvas->flush();

        skSurface->readPixels(bitmap, 0, 0);
        canvas.drawImage(SkImage::MakeFromBitmap(bitmap), 0, 0);

#if defined(SHOW_GLFW_SHADOW_WINDOW)
        SkBitmap shadowBitmap;
        shadowBitmap.allocPixels(bitmap.info());
        SkCanvas shadowCanvas(shadowBitmap);
        shadowCanvas.translate(0, 500);
        shadowCanvas.scale(1, -1);
        shadowCanvas.drawImage(SkImage::MakeFromBitmap(bitmap), 0, 0);
        glfwSwapBuffers(window);
#endif
    };

    onResize(winWidth, winHeight);

    SkiaFramework sframework;
    sframework.SetWindowWidth(winWidth);
    sframework.SetWindowHeight(winHeight);
    sframework.SetGPUAllowance(false);
    sframework.SetResizeFunc(onResize);
    sframework.SetDrawFunc(onDraw);
    sframework.Run();

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}
